﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
using ZedGraph;

namespace ClusterizationClient.View
{
    public static class Drawer
    {
        public static Color GetColor(int i)
        {
            int number = 0;
            List<Color> colors = new List<Color>();
            colors.Add(Color.Blue);
            colors.Add(Color.Red);
            colors.Add(Color.Green);
            colors.Add(Color.Orange);
            colors.Add(Color.DarkSeaGreen);
            colors.Add(Color.DeepPink);
            colors.Add(Color.DarkGray);
            colors.Add(Color.DarkSalmon);
            colors.Add(Color.BlueViolet);
            colors.Add(Color.ForestGreen);
            colors.Add(Color.GreenYellow);
            colors.Add(Color.Olive);
            Math.DivRem(i, 12, out number);
            return colors[number];
        }

        public static void Clear(ZedGraphControl control)
        {
            // Получим панель для рисования
            GraphPane pane = control.GraphPane;

            pane.XAxis.Scale.Min = 0;
            pane.YAxis.Scale.Min = 0;
            pane.XAxis.Scale.Max = 0.00001;
            pane.YAxis.Scale.Max = 0.00001;

            // Очистим список кривых
            pane.CurveList.Clear();

            // Обновляем график
            control.Invalidate();
        }


        /// <summary>
        /// Метод построения одномерного графика
        /// </summary>
        /// <param name="zedGraphControl">панель для рисования</param>
        /// <param name="dataGridView1">таблица с кластерами</param>
        /// <param name="dataGridView2">таблица с характеристиками</param>
        /// <param name="characteristic1">имя характеристики</param>
        internal static void DrawGraph(ZedGraphControl zedGraphControl, DataGridView dataGridView1, DataGridView dataGridView2, string characteristic1)
        {
            GraphPane pane = zedGraphControl.GraphPane; // Получим панель для рисования
            pane.Title.Text = "Классификация"; // Название панели и осей
            pane.XAxis.Title.Text = characteristic1;
            pane.YAxis.Title.Text = "";

            //массивы центров и границ прямоугольников
            List<double> Borders = new List<double>();
            List<double> Points = new List<double>();

            //Массивы координат центров прямоугольников разбитые по кластерам
            //и массив номеров кластеров
            List<double> BeginPoints = new List<double>();
            List<double> EndPoints = new List<double>();
            List<int> Numbers = new List<int>();

            int x = -1;
            //Ищем в таблице столбцы с заданной характеристикой
            for (int i = 2; i < dataGridView1.Columns.Count; i++)
            {
                if (characteristic1.Equals(dataGridView1.Columns[i].HeaderText))
                {
                    x = i;
                }
            }

            //Заполняем массив точек из таблицы значениями
            for (int j = 0; j < dataGridView1.Rows.Count; j++)
            {
                Points.Add(Convert.ToDouble(dataGridView1.Rows[j].Cells[x].Value));
            }
            //сортируем точки по возрастанию
            Points.Sort();
            dataGridView1.Sort(dataGridView1.Columns[x],ListSortDirection.Ascending);

            //Создаём массив для хранения идентефикаторов таксонов и заполняем его
            List<int> PointsId = new List<int>();
            for (int i = 0; i < dataGridView1.RowCount; i++)
            {
                PointsId.Add(Convert.ToInt32(dataGridView1.Rows[i].Cells[1].Value));
            }

            //Составляем массив границ прямоугольников
            Borders.Add(Points[0]);
            
            for (int k = 1; k < Points.Count; k++)
            {
                double CurrentBorder = Points[k-1] + (Points[k] - Points[k-1])/2;
                Borders.Add(CurrentBorder);
            }
            Borders.Add(Points[Points.Count-1]);

            Borders[0] -= Borders[1] - Borders[0];

            Borders[Borders.Count - 1] += Borders[Borders.Count - 1] - Borders[Borders.Count - 2];


            //Заполняем массив границ в соотвествии с номерами кластеров
            int previosCluster = -1;

            for (int i = 0; i < dataGridView1.Rows.Count; i++)
            {
                int ClusterNumber = -1;
                //ищем номер кластера текущей точки
                for (int j = 0; j < dataGridView2.Rows.Count; j++)
                {
                    if ((dataGridView2.Rows[j].Cells[1].Value == dataGridView1.Rows[i].Cells[0].Value))
                    {
                        ClusterNumber = (int)dataGridView2.Rows[j].Cells[2].Value;
                        //если текущая точка относится не к тому же кластеру что и предыдущая
                        if (ClusterNumber !=previosCluster)
                        {
                            Numbers.Add(ClusterNumber);
                            previosCluster = ClusterNumber;
                            BeginPoints.Add(Borders[i]);
                            EndPoints.Add(Borders[i + 1]);
                            break;
                        }
                        //если текущая точка из того же кластера что и предыдущая
                        else
                        {
                            EndPoints[EndPoints.Count - 1] = Borders[i + 1];
                        }
                    }
                } 
            }

            PointPairList pointPairs = new PointPairList();

            for (int m = 0; m < Points.Count; m++)
            {
                pointPairs.Add(Points[m], 0);
            }

            LineItem Elements = pane.AddCurve("Объекты", pointPairs, Color.Black, SymbolType.VDash);
            Elements.Line.IsVisible = false;
            Elements.Symbol.Size = 20;

            //чертим прямоугольник для каждого интервала
            /*for (int k = 0; k < BeginPoints.Count; k++)
            {
                    PointPairList Line = new PointPairList();
                    Line.Add(BeginPoints[k], 0);
                    Line.Add(EndPoints[k], 0);
                    LineItem myCurve = pane.AddCurve("", Line, GetColor(k), SymbolType.None);
                    myCurve.Line.Width = 100.0f;
            }*/

            //Рисуем номера таксонов
            //Создаём массив надписей
            List<TextObj> Text = new List<TextObj>();
            int level = 1;
            for (int n = 0; n < Points.Count; n++)
            {
                pointPairs.Add(Points[n], 0);
                //Чередуем надписи сверху и снизу
                if (level==1)
                {
                    //Сверху 1
                    Text.Add(new TextObj(PointsId[n].ToString(), Points[n], 0.15));
                }
                if (level==2)
                {
                    //Сверху 2
                    Text.Add(new TextObj(PointsId[n].ToString(), Points[n], 0.25));
                }
                if (level==3)
                {
                    //Снизу 1
                    Text.Add(new TextObj(PointsId[n].ToString(), Points[n], -0.15));
                }
                if (level==4)
                {
                    //Снизу 2
                    Text.Add(new TextObj(PointsId[n].ToString(), Points[n], -0.25));
                    level = 0;
                }
                //Отключаем рамочку у текста
                Text[n].FontSpec.Border.IsVisible = false;
                Text[n].FontSpec.Fill = new Fill();
                level++;
            }

            //Наносим соответсвующие названия таксонов на график
            for (int p = 0; p < Text.Count; p++)
            {
                pane.GraphObjList.Add(Text[p]);
            }

            //формируем легенду для кластеров в цикле
            for (int i = 0; i < Numbers.Count; i++)
            {
                LineItem Legend = pane.AddCurve("Кластер " + Numbers[i], new PointPairList(), GetColor(i), SymbolType.None);
                Legend.Line.Width = 10.0f;
            }

            pane.XAxis.Scale.Min = Borders[0] - (Borders[Borders.Count-1] - Borders[0])*0.05;
            pane.YAxis.Scale.Min = -1;
            pane.XAxis.Scale.Max = Borders[Borders.Count-1] + (Borders[Borders.Count-1] - Borders[0])*0.05;
            pane.YAxis.Scale.Max = 1;

            pane.XAxis.MajorGrid.IsVisible = false; // Включаем отображение сетки напротив крупных рисок по оси X
            // Задаем вид пунктирной линии для крупных рисок по оси X:
            // Длина штрихов равна 10 пикселям, ... 
            pane.XAxis.MajorGrid.DashOn = 10;
            // затем 5 пикселей - пропуск
            pane.XAxis.MajorGrid.DashOff = 5;
            // Включаем отображение сетки напротив крупных рисок по оси Y
            pane.YAxis.MajorGrid.IsVisible = false;
            // Аналогично задаем вид пунктирной линии для крупных рисок по оси Y
            pane.YAxis.MajorGrid.DashOn = 10;
            pane.YAxis.MajorGrid.DashOff = 5;
            // Включаем отображение сетки напротив мелких рисок по оси X
            //Цвет сетки
            pane.XAxis.MajorGrid.Color = Color.LightGray;
            pane.YAxis.MajorGrid.Color = Color.LightGray;
            //Оси
            pane.XAxis.MajorGrid.IsZeroLine = false;
            pane.YAxis.MajorGrid.IsZeroLine = true;
            //Обновление рисунка
            pane.AxisChange();
        }

        /// <summary>
        /// Метод построения двумерного графика
        /// </summary>
        /// <param name="zedGraphControl">панель для рисования</param>
        /// <param name="dataGridView1">таблица с кластерами</param>
        /// <param name="dataGridView2">таблица с характеристиками</param>
        /// <param name="characteristic1">имя первой характеристики</param>
        /// <param name="characteristic2">имя второй характеристики</param>
        internal static void DrawGraph(ZedGraphControl zedGraphControl, DataGridView dataGridView1, DataGridView dataGridView2, string characteristic1, string characteristic2)
        {
            GraphPane pane = zedGraphControl.GraphPane; // Получим панель для рисования
            double Xmax = 0;
            double Ymax = 0;
            double Ymin = 0;
            double Xmin = 0;
            pane.Title.Text = "Классификация"; // Название панели и осей
            pane.XAxis.Title.Text = characteristic1;
            pane.YAxis.Title.Text = characteristic2;
            List<PointPairList> list1 = new List<PointPairList>(); // Создадим список точек
            List<int> clusters = new List<int>();
            int x = -1;
            int y = -1;
            //Ищем в таблице столбцы с заданными характеристиками
            for (int i = 2; i < dataGridView1.Columns.Count; i++)
            {
                if (characteristic1.Equals(dataGridView1.Columns[i].HeaderText))
                {
                    x = i;
                }

                if (characteristic2.Equals(dataGridView1.Columns[i].HeaderText))
                {
                    y = i;
                }
            }

            //Заполняем массив кластеров запоминаем их номера
            for (int i = 0; i < dataGridView2.Rows.Count; i++)
            {
                if (!clusters.Contains((int)dataGridView2.Rows[i].Cells[2].Value))
                {
                    clusters.Add((int)dataGridView2.Rows[i].Cells[2].Value);
                    list1.Add(new PointPairList());
                }
            }

            //Заполняем массив точек в соотвествии с номерами их кластеров
            for (int i = 0; i < dataGridView1.Rows.Count; i++)
            {
                int number = -1;
                //ищем номер кластера текущей точки
                for (int j = 0; j < dataGridView2.Rows.Count; j++)
                {
                    if ((int)dataGridView2.Rows[j].Cells[0].Value == (int)dataGridView1.Rows[i].Cells[1].Value)
                    {
                        number = (int) dataGridView2.Rows[j].Cells[2].Value;
                    }
                }
                //достаём координаты соответсвующей точки из таблциы 
                //и добавляем их в массив точек на вывод соответствующего кластера
                list1[clusters.IndexOf(number)].Add((double) dataGridView1.Rows[i].Cells[x].Value,
                             (double) dataGridView1.Rows[i].Cells[y].Value);
                // указываем масштаб графика

                if (((double)dataGridView1.Rows[i].Cells[y].Value < Ymin)||(i==0))
                {
                    Ymin = (double)dataGridView1.Rows[i].Cells[y].Value;
                }

                if (((double)dataGridView1.Rows[i].Cells[x].Value < Xmin)||(i==0))
                {
                    Xmin = (double)dataGridView1.Rows[i].Cells[x].Value;
                }

                if ((double) dataGridView1.Rows[i].Cells[y].Value > Ymax)
                {
                    Ymax = (double) dataGridView1.Rows[i].Cells[y].Value;
                }

                if ((double) dataGridView1.Rows[i].Cells[x].Value > Xmax)
                {
                    Xmax = (double)dataGridView1.Rows[i].Cells[x].Value;
                }
            }
            LineItem myCurve = null;
            for (int i = 0; i < list1.Count; i++)
            {
                myCurve = pane.AddCurve("Кластер " + clusters[i], list1[i], GetColor(i), SymbolType.Diamond);
                myCurve.Line.IsVisible = false;
                myCurve.Symbol.Fill.Type = FillType.Solid;
                myCurve.Symbol.Size = 5;
            }

            //Создаём отступ по краям графика
            double Xdiff = Xmax - Xmin;
            double Ydiff = Ymax - Ymin;

            Xmin -= Xdiff*0.05;
            Ymin -= Ydiff*0.05;
            Xmax += Xdiff * 0.05;
            Ymax += Ydiff * 0.05;

            pane.XAxis.Scale.Min = Xmin;
            pane.YAxis.Scale.Min = Ymin;
            pane.XAxis.Scale.Max = Xmax;
            pane.YAxis.Scale.Max = Ymax;

            pane.XAxis.MajorGrid.IsVisible = true; // Включаем отображение сетки напротив крупных рисок по оси X
            // Задаем вид пунктирной линии для крупных рисок по оси X:
            // Длина штрихов равна 10 пикселям, ... 
            pane.XAxis.MajorGrid.DashOn = 10;
            // затем 5 пикселей - пропуск
            pane.XAxis.MajorGrid.DashOff = 5;
            // Включаем отображение сетки напротив крупных рисок по оси Y
            pane.YAxis.MajorGrid.IsVisible = true;
            // Аналогично задаем вид пунктирной линии для крупных рисок по оси Y
            pane.YAxis.MajorGrid.DashOn = 10;
            pane.YAxis.MajorGrid.DashOff = 5;
            // Включаем отображение сетки напротив мелких рисок по оси X
            //Цвет сетки
            pane.XAxis.MajorGrid.Color = Color.LightGray;
            pane.YAxis.MajorGrid.Color = Color.LightGray;
            //Оси
            pane.XAxis.MajorGrid.IsZeroLine = true;
            pane.YAxis.MajorGrid.IsZeroLine = true;

            pane.AxisChange();
        }
    }
}
